package OliveStory;

# Copyright (c) 2005, Shawn Boyette
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in
#   the documentation and/or other materials provided with the
#   distribution.
#  * Neither the name of the Olive Newsreader  nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

require Exporter;
use warnings;
use strict;
use Date::Calc qw(Day_of_Week Mktime Timezone Delta_DHMS);
use Encode;
use OliveWindow;

our @ISA       = qw(Exporter);
our @EXPORT    = qw( &markall &refreshlist &storyprev &storynext &shiftlist );


#######################################################################
##  story display logic  ##############################################
#######################################################################

sub markall {
    # sets the 'read' status of all db entries to true or false
    # depending on its second argument
    my ($cui,$ru) = @_;

    my $d = $cui->userdata->{dbh};
    my $statement = "UPDATE stories SET read = $ru WHERE id > 0";
    my $sth = $d->prepare($statement);
    $sth->execute;

    refreshlist($cui);
}

sub refreshlist {
    # (re)generates the olive story list

    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};
    my $d = $cui->userdata->{dbh};
    my $x = $w->{dim}[0];
    my @values = ();
    my %labels = ();
    my $select = -1;

    my @t = gmtime(( time() - ($cui->userdata->{tz} * 3600)));
    ($cui->userdata->{ur}) = $d->selectrow_array("SELECT count(id) FROM stories WHERE read = 0");

    my $statement = "SELECT id,title,nick,timestamp,read FROM stories order by ";
    $statement   .= "read, " if $c->{utt};
    $statement   .= "timestamp DESC";
    $statement   =~ s/ DESC$// if $c->{sof};
    my $sth = $d->prepare($statement);
    $sth->execute;
    my $i = 0;
    my $fill = $x - 26;
    while (my $q = $sth->fetchrow_arrayref) {
        my @t2 = gmtime($q->[3]);
        my $timestring = '';
        if ($q->[3] == 0) { # no time given in feed
            $timestring = "    ???";
        } else {
            my ($Dd,$Dh,$Dm,$Ds) = Delta_DHMS(($t2[5]+1900),($t2[4]+1),$t2[3], $t2[2],$t2[1],$t2[0],
                                              ($t[5]+1900), ($t[4]+1), $t[3],  $t[2], $t[1], $t[0]);
            $timestring = sprintf("%02dh %02dm",$Dh,$Dm);
            $timestring = sprintf("%02dd %02dh",$Dd,$Dh) if $Dd;
            $timestring = "   +99d" if ($Dd > 99);
        }

        my $nick = sprintf("%-16s ",$c->{feeds}{$q->[2]}{disp});

        $q->[1] = "[Null]" unless (defined $q->[1]);
        my $label = $q->[1];
        $label = substr($label,0,$fill - 1);
        Encode::_utf8_on($label) if ($ENV{LC_ALL} and ($ENV{LC_ALL} =~ /UTF-8/));
        $label = $label . (' ' x ($fill - length($label)));

        my $rf = $q->[4] ? ' -' : ' @'; # read/unread indicator
  
        $values[$i] = $q->[0];
        $labels{$q->[0]} = $label . $nick . $timestring . $rf;
        $i++;
    }

    $select = $cui->userdata->{nlsel} if $cui->userdata->{nlsel};

    $w->{news}->delete('newslist') if (defined $w->{news}{list});
    $w->{news}{list} = $w->{news}->add('newslist', 'Listbox',
                                       -y      => 1,
                                       -values => \@values,
                                       -labels => \%labels,
                                       -height     => int($w->{dim}[1] / 2) - 2,
                                       -selected   => $select,
                                       -vscrollbar => 1,
                                       -onchange   => sub { storypick($cui) },
                                       -onselchange=> sub { winstatus($cui) if $c->{ws} },
                                      );
    winflip($cui,'story') unless ($cui->userdata->{focus} eq 'story');
    winstatus($cui) if $c->{ws};
}

#-------------------------------------------------------------

sub shiftlist {
    my $cui = shift;
    my $w = $cui->userdata->{wins};

    $cui->userdata->{nlsel} = 0;

    my $halfheight = int($w->{news}{list}->canvasheight /2);
    my $i = my $j = 0;
    for $j (1..$halfheight - 1) {
        last if ($w->{news}{list}->{-ypos} == $w->{news}{list}->{-max_selected});
        $w->{news}{list}->option_next;
        $i++;
    }
    for (1..$i) {
        $w->{news}{list}->option_prev;
    }
    $i = 0;
    for $j (1..$halfheight) {
        last if ($w->{news}{list}->{-ypos} == 0);
        $w->{news}{list}->option_prev;
        $i++;
    }
    for (1..$i) {
        $w->{news}{list}->option_next;
    }
    $w->{news}{list}->draw;
}

#-------------------------------------------------------------

sub storynext {
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};
    
    $w->{news}{list}->option_next unless $c->{utt};
    my $id = $w->{news}{list}->get_active_id;

    # handle skip-read
    if ($c->{sr} and !$c->{utt}) {
        my $d = $cui->userdata->{dbh};
        my $rid = $w->{news}{list}{-values}[$id];
        my ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
        my $lastid = -1;
        while ($read) {
            $w->{news}{list}->option_next;
            $rid = $w->{news}{list}{-values}[$w->{news}{list}->get_active_id];
            ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
            last if ($lastid == $rid); # infinite loop escape
            $lastid = $rid;
        }
        $id = $w->{news}{list}->get_active_id;
    }

    # set new selection
    $w->{news}{list}->set_selection($id);
    # and force display if utt is in effect (it's already selected so
    # the callback from refreshlist doesn't do anything.
    storypick($cui) if $c->{utt}; 
}

#-------------------------------------------------------------

sub storyprev {
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};

    $w->{news}{list}->option_prev;
    my $id = $w->{news}{list}->get_active_id;

    # handle skip-read
    if ($c->{sr} and !$c->{utt}) {
        my $d = $cui->userdata->{dbh};
        my $rid = $w->{news}{list}{-values}[$id];
        my ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
        my $lastid = -1;
        while ($read) {
            $w->{news}{list}->option_prev;
            $rid = $w->{news}{list}{-values}[$w->{news}{list}->get_active_id];
            ($read) = $d->selectrow_array("SELECT read FROM stories WHERE id = $rid");
            last if ($lastid == $rid); # infinite loop escape
            $lastid = $rid;
        }
        $id = $w->{news}{list}->get_active_id;
    }

    # set new selection
    $w->{news}{list}->set_selection($id);
}

#-------------------------------------------------------------

sub storypick {
    my $cui = shift;
    my $w = $cui->userdata->{wins};
    my $c = $cui->userdata->{c};
    my $d = $cui->userdata->{dbh};
    
    my $sel = $w->{news}{list}->get;
    my $id  = $w->{news}{list}->get_active_id;
    return if (!defined $sel or $sel eq ''); # rare case: someone hitting enter with no feeds 
    

    $w->{news}{ftr1}->text('Fetching...' . ' ' x ($w->{dim}[0] - 11));
    $w->{news}{ftr1}->draw;

    my @story = $d->selectrow_array("SELECT nick,title,timestamp,desc,link,read FROM stories WHERE id = $sel");

    unless ($story[5]) { # if unread...
        my $statement = "UPDATE stories SET read = 1 WHERE id = $sel";
        my $sth = $d->prepare($statement);
        $sth->execute;
        # sekrit message passing for menu rebuild
        $cui->userdata->{nlsel} = $id;
        refreshlist($cui);
    }

    $cui->userdata->{chan} = $c->{feeds}{$story[0]}{title};

    $w->{news}{ftr1}->text(' ' x $w->{dim}[0]);
    winstatus($cui) if $c->{ws};
    $w->{news}{ftr1}->draw;

    # just in case (especially titles)
    $story[1] = "[NULL]" unless (defined $story[1]);
    $story[3] = "[NULL]" unless (defined $story[3]);

    # paint story
    Encode::_utf8_on($story[1]) if ($ENV{LC_ALL} and ($ENV{LC_ALL} =~ /UTF-8/));
    $story[3] = (' ' x int(($w->{dim}[0] - length($story[1])) / 2)) . $story[1] . "\n\n" . $story[3] 
        if $c->{utt};
    $story[3] = $story[3] . "\n\n--\n" . $story[4];
    $w->{story}{view}->text($story[3]);
    $w->{story}{view}->cursor_to_home;
    $w->{story}{view}->draw;

    # window housekeeping
    winflip($cui,'news') if ($c->{fol});
    shiftlist($cui);
    $w->{news}{list}->clear_selection if $c->{utt};
}


1;
